供应商模块 API 接口规范
发行版 v2.1 · 基线 v2.0 / 前端联调版 2026-08-25 · 已部署 TEST
供前端联调使用;契约基线来自 v2.0,运行状态以当前 TEST 验收结论为准。
后端 only
统一 Result<T>
本期 LOCAL_AUTO 审批 · TEST
后续 WECOM 对接
敏感字段严格边界
OpenAPI 3.0 内嵌
单文件离线 HTML
0. 文档结论
前端联调状态:本版以当前 TEST 验收结果为准。15 个接口已实现并可联调;SUP-ADM-010 已实现权限门禁和失败关闭,但 Finance 清账提供方未接入,当前不可成功归档。
| 接口范围 | 状态 | 前端接入说明 |
|---|---|---|
| SUP-ADM-001、002、003、004、007、011、012、043 | 已实现 · 可联调 | 按接口卡片请求、响应、权限和错误码接入 |
| SUP-ADM-034、035、036、041 | 已实现 · 可联调 | 账户证明字段按权限裁剪;账号仅返回脱敏值 |
| SUP-ADM-048、049、050 | 已实现 · 可联调 | 资源页面调用;改绑/解绑必须携带并发版本字段 |
| SUP-ADM-010 | 已实现 · 失败关闭 | 权限通过后仍返回 395032;清账提供方就绪前不要按成功链路联调 |
本文收录本期全部 16 个 HTTP 契约,并补充当前实现状态、权限、TEST 结论与前端接入限制。
15 个接口已实现并可联调;SUP-ADM-010 仅保留失败关闭行为,清账提供方未接入前不可成功联调。
审批分期冻结:本期完整实现审批表、候选快照、状态机、审计和统一结果应用器,审批提供方固定为
LOCAL_AUTO。
本地提供方返回真实的标准化 APPROVED 结果,由统一结果应用器完成主体、账户、资质和状态变更;不得伪造 spNo、企微模板、审批人、部门、意见、原始企微状态或回调。
后续仅新增 WECOM 提供方适配器、Feign/回调/对账,不改审批表主模型、业务状态机和结果应用逻辑。
可生成范围:本文件用于前端联调,不作为代码生成输入。实现状态以每张接口卡片和下方验收矩阵为准;不得据此推断归档成功链路已开放。
- 供应商为资源管理之前的独立一级模块,管理端基址为
/admin/supplier。 - 供应商菜单、页面与按钮由平台配置
supplier:*权限码;服务端逐接口校验权限并失败关闭,不定义固定本地角色。类型、信用、状态、审批意见和账户证明附件使用独立权限码。 - 本地不提供 approve、reject 或“我的审批待办”;审批人在企业微信处理。转交、加签和同意并加签本期不建模。
- 企业微信 Supplier 模板必须增加申请人、必需审批节点和实际审批人的受控部门字段;外部适配器按 controlId 从表单/详情同步,完整追溯统一保存到单个版本化
detail_snapshot_ciphertext,禁止用当前通讯录补写。 - Gateway 路由与最小权限已在 #6191/#6194 完成并经 TEST 验证。
- 注册主体允许 0..N 个初始账户;没有账户也能完成注册,但不具备付款资格。
- 供应商页面统一使用平台字典并按需加载:供应商类型读取
supplier_type(GET /admin/dict/data/supplier_type),生命周期读取supplier_lifecycle_status(GET /admin/dict/data/supplier_lifecycle_status);业务接口只传编码,不传中文标签或字典数据 ID。 - 接口文档冻结 API 与业务逻辑;本期物理表统一使用详细设计冻结的
supplier_*,不增加resource_前缀。Entity、Mapper、DDL 和索引仍须在新任务 worktree 中核对当前 migration 与实际 schema。
1. 范围与事实依据
| 来源 | 负责范围 | 使用方式 |
|---|---|---|
| 供应商模块详细设计 v3.0 | 接口、权限、状态、业务规则、审批、幂等、DTO/VO | 接口契约主依据 |
| 数据模型 | 字段、类型、必填、枚举、默认值、唯一性、索引与迁移边界 | 从详细设计 v3.0 数据库章节抽取;字段基线同步《数据模型》v1.9.1,物理落表范围以当前数据模型为准 |
| HL 供应商专项硬规则 | 后端边界、平台菜单/按钮权限、删除、日志、菜单缓存 | 最高项目边界,不被普通需求放宽 |
严格收敛规则
- 路径、方法、平台权限码、状态、审批语义以详细设计 v3.0 为准。
- 字段上限以本文件“共享 DTO / VO 字典”的冻结值为准,并与最新详细设计的字段修订保持一致;不得再回退到历史较小值。
- 审批完整快照、幂等、防乱序、可靠事件采用 v3.0 的更严格增量。
- 账号掩码不落库;物理表前缀统一为详细设计冻结的
supplier_*。对外契约锁定“永不返回完整账号”。 - 本文件未定义的枚举、缓存和降级语义不得自行补造;供应商错误码已冻结为资源服务共享段中的
395xxx子段。
本期不做
- 批量导入、导入批次、导入明细、失败清单和失败重导;本期同时不建导入表、不生成 Entity/Mapper/DTO/Controller/Service/Job、空实现或假成功。
- 履约评价自动采集与信用等级自动聚合。
- 本地审批流、审批按钮、审批待办。
- 供应商域中的应付、付款、团核算、余额和付款金额算法。
- 任何前端源码或资源修改。
1.1 后端代码生成冻结规则
生成结论:本文件是本期供应商后端 API 与业务逻辑的单一生成输入。OpenAPI 冻结路径、方法、认证、operationId 和核心 Schema;
每张接口卡片中的“业务、状态与副作用”、权限矩阵、状态机、错误码和验收矩阵共同冻结 Service 行为。若与当前源码、测试、migration 或实际 schema 冲突,必须停止生成冲突部分并先完成契约审计。
允许生成
hl-resource-service下 supplier 后端包的 Controller、DTO/VO、Application Service、Domain Guard、Mapper 接口与测试骨架。- 允许在 supplier 后端包下按同一业务功能新建子包,将相关 Controller、DTO/VO、Application Service、Domain Guard、Mapper、集成适配器及测试集中管理;测试包结构应与源码对应,不得新建无业务边界的顶级模块,也不得把无关功能混入同一目录。
- supplier 包内的出站 Port、本地适配器接口与测试桩;外部能力不可用时必须失败关闭。
- Supplier 企微闭环所需的
hl-common-core公共 DTO、现有ApprovalFeignClient新方法与 fallback、hl-user-service提供方委托/详情规范化/回调调用方及双方契约测试;旧方法签名保持兼容。
禁止自动生成
- 任何管理端、小程序、Web、H5 或桌面端源码和资源。
- 未经当前 migration/schema 审计的 Entity 表名、跨 schema 写库、共享库 DDL、Flyway 自动开启。
本方案边界
- Supplier 主体业务施工位于
hl-resource-service的 supplier 后端包及所属 schema migration;Gateway 路由仍作为独立后端任务。VEHICLE 只依赖 Fleet 内部只读能力,本文不冻结新路径;实现前先审计现有契约,不足部分另行评审 common Feign 与 Fleet 提供方变更。 - 本期定义
SupplierApprovalProviderSPI 并只注册LocalAutoApprovalProvider。提交先持久化审批实例和候选快照,再由本地提供方返回标准化通过结果,最后由公共SupplierApprovalResultApplier锁行、复核并应用;禁止 Controller/Service 直接跳过审批表改状态。 - 后续
WeComApprovalProvider才复用/扩展现有ApprovalFeignClient、回调和企微配置。管理端始终不得传 provider、模板、级次或审批人。
后续 WECOM 配置键预留(本期不读取)
approval.supplier.profile.template-id:PROFILE_CREATE。approval.supplier.account.template-id:ACCOUNT_CREATE / ACCOUNT_CHANGE;账户事实包含 settleMode/accountPeriod/invoiceType/taxRate。approval.supplier.status.template-id:bizType=STATUS_CHANGE;subType=STATUS_SUSPEND / STATUS_RESUME / STATUS_FREEZE / STATUS_UNFREEZE / STATUS_BLACKLIST / STATUS_UNBLACKLIST。approval.supplier.*.control-ids.*:requestNo、supplierId、bizType、subType、摘要、applicantDepartments、levelDepartments、approverDepartments 等控件 ID;启动时完整校验,缺失返回 395019。
OpenAPI 3.0 机器契约
仅导出本期 16 个 HTTP operation。所有 operation 都带 x-hl-access、x-hl-permissions、x-hl-rules、x-hl-errors 和原始请求/响应契约,供代码生成器同时生成结构与强制业务策略;不能只读取 path 而忽略扩展字段。管理端菜单和按钮由平台配置,服务端统一调用现有 UserFeignClient.hasPermission 校验权限码且异常失败关闭,不再生成固定角色判断。
契约基线扩展:沿用 codegen-r26 的全部审批、账户、软删除和路径规则;保留
supplier_resource_rel 作为统一关系表,但维护入口全部归属资源模块及车队模块。供应商详情只有“基本信息、账号信息”两个页签,不生成资源候选、供应商侧绑定或批量解绑接口;资源页面复用供应商分页/有界列表选择供应商,并通过 SUP-ADM-048~050 查询、设置/改绑或解除关系。同一资源只允许一个有效供应商。VEHICLE 对应菜单“车队管理-车队管理”,仅通过 Fleet 内部只读能力校验,禁止跨 schema SQL,具体 Fleet 接口路径在实施审计后确定。
| 策略 | 生成目标 | 不可替代项 |
|---|---|---|
| Guard | Permission/Profile/Type/Account/Approval/Qualification/Clearance Guard;Controller 不写固定角色 if-else | 可信身份、平台权限码、归属、软删、状态、expectedUpdateTime、字段规则和失败零副作用 |
| 状态机 | Supplier/Account COLA Config + Helper + CAS Mapper + 全矩阵测试 | 未声明迁移 395005;合法自循环显式白名单;CAS miss 395014 |
| 事务 | 本地写单事务;外部副作用采用 PREPARE_TX → NO_TX_EXTERNAL → RESULT_TX | Feign/MQ/文件/企微不得在数据库事务或行锁内调用;禁止 this 自调用绕过代理 |
| 锁 | Service 代理入口 @Lock4j + 锁内 FOR UPDATE 重读 + CAS/唯一键 | 固定数据库锁序;Redis 锁不是唯一正确性来源 |
| 防重与幂等 | 管理端写接口沿用现有 @Idempotent Redis 短窗防重;审批统一使用 requestNo | 短窗防重不是持久正确性来源;同时依靠聚合锁、状态机、CAS/唯一约束、requestNo 及结果应用幂等;spNo 仅后续 WECOM 使用 |
正在生成内嵌契约…
正在生成…
生成准入门禁
- 先在新任务 worktree 中重新核对分支、工作区、Supplier 所属 migration/实际 schema,以及现有外部运行条件;外部条件不满足时只关闭对应 Port,不修改其他模块。
- Supplier 业务代码已按关联工单完成独立审计与 TEST 验收;Gateway 必须另开后端任务补充
/admin/supplier/**路由并运行路由审计。企微适配器模仿系统现有调用接口,Supplier 模板/controlId 未完成配置与实证时保持关闭,且不得标记“跨模块联调完成”。 - 只生成本期 operation;每个写接口必须实现可信身份、服务端授权、状态 Guard、锁/幂等、事务、审计和失败零副作用。
- 错误码使用本文件的逐项冻结值:395001~395039;实现后由
ErrorCodeRegistry做范围与重复 FAIL-FAST 校验。 - 跨服务写只能沿用本地事务 + 可靠事件/对账;不得同步直写其他 schema。
- 生成结果必须补齐成功、未认证、越权、缺参、边界、非法状态、重复/乱序/超时与副作用测试后,才能称“实现完成”。
2. 通用接口契约
| 管理端基址 | /admin/supplier,必须经 Gateway,认证级别 MANDATORY |
|---|---|
| 内部基址 | /internal/supplier,沿用当前 X-Internal-Token;调用方身份/白名单基础设施不在本方案新增 |
| 响应 | Result<T>:code、message、success、data;业务失败可能仍为 HTTP 200,错误时 data 允许 null |
| 分页 | PageResult<T>:records、total、page、pageSize;total/page/pageSize 与当前公共类的 int 对齐;page 默认 1,pageSize 默认 20、最大 100 |
| ID | 所有 Snowflake Long 在 JSON 中序列化为 String |
| 日期/时间 | yyyy-MM-dd / yyyy-MM-dd HH:mm:ss |
| 前端字典 | 供应商页面按需调用平台字典接口;供应商类型使用 GET /admin/dict/data/supplier_type,生命周期使用 GET /admin/dict/data/supplier_lifecycle_status。dictValue 是业务编码,dictLabel 仅用于展示。 |
| 排序 | sortBy 仅接受接口白名单;sortDirection=ASC/DESC;默认 createTime DESC, supplierId DESC |
| 并发 | 更新接口携带 expectedUpdateTime;无 body 的软删除命令锁行后重查软删除标记、当前状态与外部引用,不新增 version 字段 |
| 防重与幂等 | 管理端写接口沿用 hl-starter-protection 的 @Idempotent Redis 短窗防重,key 禁止拼接敏感明文。业务正确性由聚合锁、状态机、expectedUpdateTime/CAS、数据库唯一约束、唯一 requestNo 和结果应用器幂等保证;本期 LOCAL_AUTO 不产生 spNo,后续 WECOM 才用 requestNo/spNo 对账。 |
| 身份 | adminId、applicantAdminId、permissionCodes、createdBy、templateId、approverUserIds、spStatus 不得由管理端请求体传入。Supplier 从可信 X-Admin-Id 取得申请人,按 operation 配置的权限码服务端校验后,仅在 Supplier 出站 Port 的内部 DTO 写 applicantAdminId |
| 敏感字段 | 只允许 Body;禁止 Query/Path;请求 DTO、账号、税号、手机号、审批意见不得进入普通日志 |
统一成功包络
{
"code": 200,
"message": "成功",
"success": true,
"data": {}
}
写接口统一执行顺序
- 可信身份、平台菜单/按钮权限、数据范围校验。
- 目标存在性、归属、软删除和当前状态校验。
- expectedUpdateTime、唯一性、业务字段和敏感字段校验。
- 管理端写接口进入
@Idempotent短窗防重;锁内继续执行状态、CAS 与唯一约束校验。 - 本地事务写业务数据、变更日志和待发布事件。
- 事务提交后才调用已启用的 Supplier 出站 Port;结果不确定时进入对账,不重复发起。DOMAIN_EVENT Publisher Port 未独立接入时保持关闭。
供应商模块统一软删除:凡接口语义为删除数据库业务记录,统一写对应表的
deleted_at=当前时间,禁止执行物理 DELETE FROM supplier_*。默认列表、详情、统计、JOIN、资格校验和内部查询必须对参与查询的每张业务表分别追加 deleted_at IS NULL;原生 SQL/XML Mapper 不得依赖 MyBatis-Plus 自动过滤。归档和合同终止属于状态变化,不属于删除。审批、变更日志和审计证据不得删除。2.1 前端字典使用约定
前端接入口径:供应商页面不得在组件内写死类型或生命周期中文文案。下拉框、筛选项、表格状态和详情标签从平台字典读取;供应商业务接口的请求与响应仍只使用稳定业务编码。
| 业务字段 | 字典类型 | 用途 | 提交/匹配值 |
|---|---|---|---|
typeCode / types[].typeCode | supplier_type | 建档多选、列表筛选、列表与详情中文展示 | dictValue |
status | supplier_lifecycle_status | 列表筛选、列表与详情生命周期展示 | dictValue |
resourceModule | 后端固定枚举,非平台字典 | 资源页面路由校验和菜单原文展示 | 10 个冻结编码之一 |
查询方式
- 供应商类型按需调用
GET /admin/dict/data/supplier_type。 - 供应商生命周期按需调用
GET /admin/dict/data/supplier_lifecycle_status。 - 两个接口均经 Gateway 访问并需要管理员认证;返回的
data只包含启用项,并按sortOrder ASC、dictDataId ASC排序,页面无需再次按中文排序。
// GET /admin/dict/data/supplier_lifecycle_status
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{ "dictType": "supplier_lifecycle_status", "dictLabel": "草稿", "dictValue": "DRAFT", "sortOrder": 10, "status": "ACTIVE" },
{ "dictType": "supplier_lifecycle_status", "dictLabel": "注册审核中", "dictValue": "VETTING", "sortOrder": 20, "status": "ACTIVE" },
{ "dictType": "supplier_lifecycle_status", "dictLabel": "合作中", "dictValue": "ACTIVE", "sortOrder": 30, "status": "ACTIVE" }
]
}
组件映射规则
const options = dictData.map(item => ({
label: item.dictLabel,
value: item.dictValue
}))
// 展示:用业务响应中的 code 匹配 dictValue,再显示 dictLabel
// 提交:只提交 value(例如 ACTIVE / HOTEL),不得提交中文、dictDataId 或整条字典对象
- 类型字段:创建、更新和提交注册表单只发送
types: [{ typeCode: "HOTEL" }];请求不接收typeName。列表和详情响应中的typeName是服务端显示快照,不能作为业务判断依据。 - 生命周期字段:列表筛选提交
status编码;状态变更按钮仍必须按第 4 节状态机收窄合法目标,不能把 7 个字典项全部当作可跳转状态。 - 资源模块:
resourceModule不是 supplier_type 字典;固定为 SCENIC、RESTAURANT、SUPPLIES、SUPPLIES_COMBO、ACTIVITY、HOTEL、SERVICE、COST_ITEM、STAFF、VEHICLE,VEHICLE 的菜单原文为“车队管理-车队管理”。FLEET仍可作为供应商类型/资格编码,但供应商侧不新增车队或挂接车辆。 - 同名状态:字典接口数据项自身的
status=ACTIVE表示“该字典项启用”,不是供应商处于合作中;供应商生命周期必须读取该项的dictValue。 - 未知编码:未匹配到字典时显示原始编码并上报契约异常,不得显示错误中文或静默改写业务值。
- 缓存更新:沿用平台现有字典缓存刷新机制;字典管理修改后不得另维护供应商页面私有枚举副本。
当前启用值(2026-08-21)
| 字典类型 | 按排序号冻结的当前值 |
|---|---|
supplier_type | SCENIC=景区,RESTAURANT=餐厅,SUPPLIES=备品,ACTIVITY=游玩项目,HOTEL=酒店,SERVICE=服务,FLEET=车队,RENTAL=租车,PERFORMANCE=演艺,INSURANCE=保险,CHANNEL=OTA/渠道,PROFESSIONAL_SERVICE=专业服务,PROPERTY=物业/房租,TICKET=票务,OTHER=其他 |
supplier_lifecycle_status | DRAFT=草稿,VETTING=注册审核中,ACTIVE=合作中,SUSPENDED=暂停,FROZEN=冻结,BLACKLIST=黑名单,ARCHIVED=清账归档 |
上表用于契约审阅和回归测试;运行时选项仍以字典接口返回的启用项为准。业务状态机和服务端枚举校验不因字典标签修改而改变。
3. 权限与字段可见性
| 业务能力 | 平台菜单/按钮权限 | 服务端规则 |
|---|---|---|
| 供应商列表与两类详情 | supplier:list / supplier:view | 查看未删除供应商及脱敏字段;供应商详情仅包含基本信息和账号信息 |
| 创建、更新、删除草稿 | supplier:create / supplier:update / supplier:delete | 同时校验状态机、归属、并发版本和失败零写入 |
| 类型及资质规则维护 | supplier:type:manage | 查看已删除类型另需 supplier:type:history |
| 审批提交 | supplier:approval:submit | 供应商注册审批和账户审批均不提供撤销接口;本地不提供通过/驳回 |
| 账户和状态维护 | supplier:account:manage / supplier:status:manage | 不同能力独立授权,不因拥有任一权限自动获得其他能力 |
| 资源侧供应商维护 | supplier:update | 仅由资源与车队模块发起;同时校验供应商状态、类型、必备资质、资源存在性、数据范围和单资源唯一有效供应商;VEHICLE 依赖失败时关闭 |
| 审批意见正文 | supplier:approval:opinion | 无权限仅返回 hasOpinion;有权限按需解密并记录敏感读取审计 |
| 账户证明附件 | supplier:account:proof:read | 无权限省略 proofFileUrls,账户列表始终不返回 |
| 本地通过/驳回 | 不配置 | 只能在企业微信办理;转交、加签和同意并加签本期不建模 |
列表可见范围:管理员获得
supplier:list/supplier:view 菜单权限后,可查看全部未删除供应商及脱敏账户,不按创建人或供应商类型过滤;未认证或未配置对应权限的账户不可查看,任何管理员都不能通过管理端取得完整账号。附件可见范围:账户列表永不返回 proofFileUrls;账户单项详情只有同时具备
supplier:view 与 supplier:account:proof:read 的管理员返回证明附件,否则仅返回其他脱敏信息。平台权限口径:供应商不定义固定本地角色矩阵。平台按菜单/按钮配置
supplier:* 权限码;企微模板中的“财务领导”“公司领导”仅是 OA 审批节点,不映射为 HL 固定角色。权限代码生成:每个
/admin/supplier/** operation 必须读取 x-hl-permissions,用可信 X-Admin-Id 调用现有 com.hulalv.common.feign.UserFeignClient.hasPermission(Long,String)。返回 false、非成功 Result、超时或异常全部拒绝;服务端只按权限码授权。高风险动作:类型、信用、状态、审批意见和账户证明附件分别使用独立按钮权限码;是否可操作完全由平台授权结果决定,不生成固定角色策略或角色上限。
隐藏按钮不是授权。未配置权限的管理员直接构造请求时,服务端必须拒绝,且数据库、企微出站 Port 与 DOMAIN_EVENT 均为零副作用。
4. 状态机与可用性
供应商七态总流程图
正常迁移/恢复审批驳回
拉黑与解除Finance 清账归档
流程图可左右滑动查看完整节点。
总门禁:注册提交先执行 C-02/C-06/C-09,有初始账户时再逐条执行 C-03/C-19。除无 body 的清账归档外,其他状态命令必须校验角色、当前状态、changeReason、expectedUpdateTime;清账归档由服务端锁行重读当前状态并生成审计上下文。任何状态命令都必须在结果应用前锁行重查,未画出的迁移统一返回
SUPPLIER_STATUS_TRANSITION_INVALID。| 当前状态 | 状态内操作(不发生迁移) | 合法目标状态 | 禁止/硬约束 |
|---|---|---|---|
草稿DRAFT | 编辑档案、类型、资质、联系人和 0..N 初始账户;可软删除 | VETTING(提交 PROFILE_CREATE) | 不得供业务选择、付款、生成 supplierNo 或发起注册后独立账户审批 |
注册审批中VETTING | 查看注册进度;可按 1.5 更新非主体标识资料 | ACTIVE(注册最终通过);DRAFT(注册驳回) | 不得修改 fullName、taxNo 或已提交审批快照 |
合作中ACTIVE | 维护联系人、类型、资质规则和注册后账户 | SUSPENDED(暂停);FROZEN(冻结);BLACKLIST(拉黑) | 不得修改红字段或直接删除 |
暂停合作SUSPENDED | 查看、整改资料;整改本身仍保持 SUSPENDED | ACTIVE(资质有效后恢复);BLACKLIST(拉黑);ARCHIVED(Finance 清账确认后归档) | 不得新付款、新业务选择或删除 |
风险冻结FROZEN | 查看、补资质;整改本身仍保持 FROZEN | ACTIVE(核验恢复);BLACKLIST(拉黑) | 不得新付款、新业务选择或直接归档 |
黑名单BLACKLIST | 查看历史与整改材料 | SUSPENDED(解除两级审批通过);ARCHIVED(Finance 清账确认后归档) | 不得直接进入 ACTIVE、新付款、新资源关联或删除 |
已归档ARCHIVED | 只读查看历史档案、账户和引用 | 无 | 不可逆;不得恢复、编辑、删除或发起新交易 |
“状态内操作”不是新状态;只有“合法目标状态”列表示状态变化。任何未在详细设计状态图中的迁移均拒绝。
5. 接口目录与统一契约卡片
6. 共享 DTO / VO 字典
以下字段是代码生成冻结值,已同步最新详细设计字段修订。
String(TEXT) 表示物理字段为 MySQL TEXT;服务端必须按各请求明文上限及 UTF-8 字节数 ≤ 65,535 双重校验。内嵌 OpenAPI 的 components.schemas 是完整机器契约:所有本期请求体均关闭 additionalProperties,所有 Query/Header 均结构化;所有 200 响应为“具体成功 Result<T> 或业务错误包络”的 oneOf,错误 data 可为 null,不得退化为 Map/Object。SupplierDraftUpsertRequest
| 字段 | 类型/上限 | 草稿 | 严格规则 |
|---|---|---|---|
| fullName | String(1..500) | 必填 | 执照主体;规范化;审批通过后不可改 |
| shortName | String(0..300) | 选填 | 列表和搜索 |
| taxNo | String(UTF-8 1..64 bytes) | 必填 | 仅请求明文;规范化后按 UTF-8 字节校验;C-02 全库唯一;tax_no 以 TEXT + ascii_bin 保存确定性密文;日志禁止 |
| types | List<SupplierTypeCodeInput>(1..15) | 必填 | 供应商类型列表;输入项只包含 typeCode。前端选项来自 supplier_type,以 dictValue 写入 typeCode;typeName 仅由服务端在响应中返回 |
| legalRepresentative | String(0..500) | 选填 | 允许通过更新供应商接口维护 |
| contactPhone | String(0..20) | 选填 | 法人电话/公司电话;加密/脱敏 |
| establishDate | LocalDate | 选填 | 不得晚于当前日期 |
| registeredCapital | String(0..50) | 选填 | 文本口径 |
| businessScope | String(0..500) | 选填 | 经营范围 |
| address | String(0..500) | 选填 | 地址 |
| staffScale | String | 选填 | LT50/R50_200/R200_500/GT500 |
| mainCooperation | String(TEXT,1..65,535 UTF-8 bytes) | 必填 | 主要合作内容;使用 UTF-8 字节校验器 |
| licenseImageUrl | String(0..500) | 草稿可空 | 提交时按 C-06 必填;必须是授权 OSS |
| approveNote | String(MEDIUMTEXT,UTF-8 ≤ 16,777,215 bytes) | 选填 | 审批说明;对应 approve_note MEDIUMTEXT |
| remark | String(MEDIUMTEXT,UTF-8 ≤ 16,777,215 bytes) | 选填 | 备注;对应 remark MEDIUMTEXT |
| contacts | List<SupplierContactInput> | 选填 | 逐条校验 |
| qualifications | List<SupplierQualificationInput> | 选填 | 提交时按所有类型必备规则并集执行 C-09;每项以 permanentValid + expiryDate 表达有效期 |
| initialAccounts | List<SupplierBankAccountInput>(0..N) | 选填 | 仅注册草稿;随 PROFILE_CREATE 共审 |
| duplicateConfirmToken | String | 条件必填 | 存在近似候选时使用;不接受 force=true |
| expectedUpdateTime | LocalDateTime | 更新供应商必填 | 格式 yyyy-MM-dd HH:mm:ss;创建草稿不传 |
更新边界:
SupplierUpdateRequest 是独立的增量补全请求,不再继承全量建档 DTO;主体标量字段按 PATCH 语义更新。types、contacts、qualifications、contracts、evaluations 未传时保持不变,一旦传入则代表该集合的完整当前快照:同 ID 项更新、无 ID 项新增、数据库有效记录中未出现在请求内的项写 deleted_at 软删除。联系人、资质、合同、评价传空数组表示软删除该集合全部有效记录;类型至少保留一个,types=[] 参数校验失败。除 changeReason、expectedUpdateTime 外至少提交一个实际变化字段。未提交草稿允许修改 fullName、taxNo;进入审批中或审批完成后,两字段只允许原值回传,任何实际变化均拒绝。请求不接收 creditLevel 或账户字段。资质有效期契约:
permanentValid=true 时 expiryDate 必须为空,permanentValid=false 时 expiryDate 必填,矛盾组合返回 SUPPLIER_QUALIFICATION_VALIDITY_INVALID(395042) 且零写入。历史完整请求未传 permanentValid 时按 expiryDate 是否为空推导;增量更新的既有资质同时省略两字段时保持原有效期。详情固定返回 permanentValid,并按 Asia/Shanghai 当前自然日动态返回有符号 daysUntilExpiry;永久有效时天数为 null。嵌套输入
| DTO | 字段 | 严格规则 |
|---|---|---|
| SupplierContactInput | contactId?、contactName(1..500)、contactPhone(1..20)、contactRole、remark(0..200) | 既有 ID 必须属于当前供应商;电话不回显明文 |
| SupplierQualificationInput | qualificationId?、qualType(1..64)、certNo(0..128)、imageUrl(TEXT,0..65,535 UTF-8 bytes)、permanentValid?、expiryDate? | permanentValid=true 时 expiryDate 必须为空,false 时 expiryDate 必填;未传时按 expiryDate 是否为空兼容推导。isRequired 由服务端规则派生,客户端不得传 isRequired |
| SupplierBankAccountInput | accountType、bankName(1..500)、bankBranch(0..500)、accountNo(UTF-8 1..128 bytes)、proofFileUrls?(0..20,每项 1..1000)、settleMode?、accountPeriod?、invoiceType?、taxRate? | CORPORATE/PERSONAL;结算字段按账户保存;MONTHLY 才允许 accountPeriod,SPECIAL/NORMAL 必填 taxRate,NONE 时 taxRate 为空;accountName 由主体全称派生;无 confirmAccountNo/accountNoMask/isPersonal |
| SupplierTypeMergeInput | typeCode、isPrimary? | 集合传入后按 (supplierId,typeCode) 对账;已存在则更新,不存在则新增,未出现在本次完整快照中的有效关联写 deleted_at |
| SupplierContactMergeInput | contactId?、联系人字段?、expectedUpdateTime? | 已有项必须同时携带 contactId/expectedUpdateTime;新增项不传两字段;集合快照中缺失的既有联系人软删除 |
| SupplierQualificationMergeInput | qualificationId?、资质字段?、permanentValid?、expiryDate?、expectedUpdateTime? | 已有项必须同时携带 qualificationId/expectedUpdateTime;既有项同时省略 permanentValid/expiryDate 时保持当前有效期;新增项不传 ID/版本且双省略时兼容为永久有效;集合快照中缺失的既有资质软删除 |
| SupplierContractMergeInput | contractId?、合同字段?、expectedUpdateTime? | 已有项必须同时携带 contractId/expectedUpdateTime;新增项不传两字段;集合快照中缺失的既有合同软删除 |
| SupplierEvaluationMergeInput | evaluationId?、评价字段?、expectedUpdateTime? | 已有项必须同时携带 evaluationId/expectedUpdateTime;新增项不传两字段;集合快照中缺失的既有评价软删除 |
供应商响应
| VO | 核心字段 | 禁止字段 |
|---|---|---|
| SupplierListItemVO | supplierId/no、full/shortName、types、status、creditLevel/totalScore、activeAccountCount、create/updateTime | 税号/证件/电话/账号明文 |
| SupplierBasicInfoVO | 主体公开业务字段、证件 mask、types、contacts mask、qualifications mask(含类型中文名、permanentValid、daysUntilExpiry、有效状态及只读 isRequired)、信用、状态、updateTime | isRelatedParty、approveNote、完整账号 |
| SupplierAccountInfoVO | supplierId、bankAccounts(含当前生效结算口径及账号 mask)、updateTime | 完整账号、密文、候选账号与往来数据 |
| SupplierResourceRelationVO | relationId、supplierId/no/name、resourceModule/moduleName、resourceId/name、requiredTypeCode/name、remark、available/reasons、create/updateTime | 资源主表名、跨服务内部字段、供应商账户与资质附件 |
| SupplierWriteResultVO | supplierId、supplierNo?、status、onboardingStage、initialAccounts[{accountId,accountNoMask,status}]、updateTime | 账号明文 |
类型、资质、联系人
| DTO | 严格字段 |
|---|---|
| QualificationRuleReplaceRequest | rules[{qualType,isRequired,warningDays≥0,sortOrder}];类型编码由路径 typeCode 提供,必须是平台字典 supplier_type 的启用 dictValue |
| ContactReplaceRequest | contacts[]、changeReason?、expectedUpdateTime |
账户与审批
| DTO | 核心字段/规则 |
|---|---|
| SupplierBankAccountVO | accountId/name/type、bank/branch、accountNoMask、settleMode/accountPeriod/invoiceType/taxRate、status、isDefault、updateTime;isDefault 使用 YES/NO 表示是/否,一个供应商可返回多条账户,但未删除账户中最多一条 isDefault=YES;永不返回密文/明文/候选值 |
| SupplierBankAccountBatchCreateRequest | accounts(1..50);每项包含 accountType、bankName(1..500)、bankBranch?、accountNo(1..128)、proofFileUrls?(0..20)、settleMode?、accountPeriod?、invoiceType?、taxRate?。一次请求可新增多个账户;每项独立生成 accountId 和 ACCOUNT_CREATE 审批,不生成可编辑草稿;户名/掩码由服务端派生 |
| BankAccountChangeRequest | bankName?、bankBranch?、accountNo?、proofFileUrls?、settleMode?、accountPeriod?、invoiceType?、taxRate?、changeReason、expectedUpdateTime;至少一个账户候选字段,accountType 不可改。结算字段与账号字段一起进入 ACCOUNT_CHANGE 候选快照,永不回传候选值 |
| ApprovalCommandResultVO | approvalLogId、requestNo、provider(本期固定 LOCAL_AUTO)、approvalStatus、spNo?/spStatus?、syncStatus、submittedAt?/finishedAt?;LOCAL_AUTO 时企微字段必须为空 |
| SupplierApprovalSnapshotDTO | schemaVersion(一期固定 1)、spNo/templateId、supplierId/approvalLogId/requestNo、bizType/subType、spStatus(String,最长 64,未知值原样保留)、applicant、apply/finishedAt、sourceRevision、detailDigest、detailSyncStatus、levels、sourceEventKey;未知版本拒绝同步和终态应用 |
其余严格命令 DTO
| DTO | 字段与必填规则 |
|---|---|
| SupplierUpdateRequest | 主体可编辑字段?、types?/contacts?/qualifications?/contracts?/evaluations?、changeReason、expectedUpdateTime;主体字段增量更新;集合字段缺省表示不处理,传入表示完整快照,缺失项及空数组对应项统一软删除;不接收 deletedIds |
| SupplierSubmitRequest | 完整复用 SupplierDraftUpsertRequest 的供应商表单字段,另含 submitNote?、expectedUpdateTime;fullName、taxNo、types、mainCooperation、licenseImageUrl、expectedUpdateTime 必填,qualifications 按 types 的必备规则条件校验 |
| SupplierStatusRequest | targetStatus、changeReason、expectedUpdateTime;服务端结合当前状态收窄合法目标 |
| SupplierResourceReassignRequest | supplierId、requiredTypeCode?、remark?、expectedCurrentSupplierId?、expectedRelationUpdateTime?、changeReason;已有关系改绑时两个 expected 字段必填 |
| SupplierResourceUnbindRequest | expectedCurrentSupplierId、expectedRelationUpdateTime、changeReason;仅由资源或车队页面解除当前资源与供应商的关系 |
| 导出 DTO | 本期不生成;请求、响应、权限与任务方式未来独立立项重新评审 |
| WeComApprovalSubmitRequest | requestNo、applicantAdminId、supplierId、approvalLogId、bizType、subType?、formFacts(oneOf);仅 SupplierApprovalPort 使用。requestNo 是唯一端到端审批号,不再传 clientRequestId;applicantAdminId 必须来自管理端入口可信 X-Admin-Id;不得携带 templateId/controlId/approver |
完整返回 VO 补充
| VO | 生产返回字段 |
|---|---|
| ArchiveResultVO | supplierId、status=ARCHIVED、clearanceRequestId、checkedAt、ledgerRevision、updateTime |
| SupplierApprovalRecordVO | changeLogId、supplierId、approvalLogId?、operationType、targetType/id?、fieldName、old/newValueMasked、changeReason、status?、operatorId?、createTime;逐行对应 supplier_change_log,不返回审批意见或敏感明文 |
| QualificationRuleVO | 规则 ID、平台字典类型编码 typeCode、资质类型、是否必备、预警天数、排序和 updateTime |
| SupplierQualificationVO / SupplierContactVO | 各自稳定 ID、业务字段、脱敏证照/电话、状态和 updateTime;不得返回敏感明文 |
| List<BankAccountSubmitResultVO> | 与请求 accounts 顺序一致;每项返回 accountId、approvalLogId、requestNo、provider、approvalStatus、syncStatus、accountStatus、isDefault=NO、submittedAt?、finishedAt?;LOCAL_AUTO 正常完成时 accountStatus=ACTIVE |
| ApprovalSnapshotAcceptVO | accepted、processStatus、sourceRevision、detailDigest、businessApplied、message? |
| WeComApprovalSubmitResultVO | spNo、applyUserName?、acceptedAt |
| WeComApprovalStatusVO | Supplier 适配器把现有 ApprovalFeignClient#getApprovalStatus 返回映射为 spNo、templateId、spStatus(String,最长 64,保留原始状态码)、申请人、applyTime;仅作轻量探测 |
7. 业务错误码(代码生成冻结)
冻结使用
hl-resource-service 的 390000–399999 共享段。供应商当前占用 395001~395039,不额外声明后续号码归供应商独占。实现 SupplierErrorCode 时标注完整资源共享段,启动和测试必须由 ErrorCodeRegistry 校验越界与重复。| 错误码常量 | 数字码 | 触发 | message |
|---|
8. 需求—接口追踪
| 需求编号 | 业务要求 | 接口覆盖 | 结论 |
|---|---|---|---|
| REQ-01 | 手工建档、草稿、提交、状态与归档 | SUP-ADM-001~004、007、010~012、043 | 完整映射 |
| REQ-02 | 供应商菜单和按钮完全由平台权限配置;类型、信用、状态、审批意见及证明附件分别使用独立权限码 | 全部管理端接口 | 服务端以可信 X-Admin-Id 调用 UserFeignClient.hasPermission,异常失败关闭;不检查固定角色名 |
| REQ-05 | 本期 LOCAL_AUTO 完整审批模型与应用 | SUP-ADM-007、010、035 | 统一 Provider SPI 与结果应用器,无伪造企微数据 |
| REQ-06 | 注册可带 0..N 初始账户;注册后新增账户直接独立提交审批;默认账户唯一 | SUP-ADM-003/004/007、034~036、041 | 完整映射;不提供注册后账户草稿、删除或停用入口 |
| REQ-07 | 锁、幂等、失败零写入、字段级审计、事件重放 | 所有写接口、SUP-ADM-012 | Supplier 内部契约统一约束;不规划外部消息链路 |
| REQ-08 | 资源与车队模块在资源页面选择、查看、改绑或解除供应商;供应商详情不维护资源 | SUP-ADM-001/043、SUP-ADM-048~050 | 统一使用 supplier_resource_rel;单资源唯一有效供应商;VEHICLE 内部只读校验 |
接口追踪没有发现“需求需要但完全无系统入口”的本期业务环节;导入和二期能力均明确标注,不伪装成当前接口。
9. 双来源一致性与内部实现边界
| 差异 | API 层处理 | 不可放宽项 |
|---|---|---|
| 名称、银行及密文字段历史长度不同 | 采用最新冻结值:fullName 500、shortName 300、legalRepresentative 500、contactName 500、bank/branch 500;mainCooperation、typeName、extraFields、资格影像 URL、tax_no、account_no_enc 为 TEXT,其中 mainCooperation 明文限制 UTF-8 ≤65,535 bytes。tax_no/account_no_enc 的确定性密文列使用 ascii_bin,明文分别限制 UTF-8 ≤64/128 bytes | DTO、OpenAPI、Bean Validation、Entity、migration、列排序规则和前缀唯一索引必须同向;不得再使用历史较小值或只校验字符数 |
| accountNoMask 动态生成或落库 | API 只承诺掩码,不披露存储策略 | 永不返回账号明文/密文/候选值 |
| isPersonal 是否落列 | API 只接受 accountType;服务端内部派生 | 客户端不得传 isPersonal |
| 审批流水还是全量快照 | 对外采用 v3.0 的完整 levels/actions/events、完整性和版本门禁 | 只凭顶层 spStatus 不得应用终态 |
| 审批部门取当前值还是历史值 | 企业微信 Supplier 模板增加申请人、节点和实际审批人部门字段;外部适配器按 controlId 从审批表单/详情解析为 ApprovalDepartmentSnapshot,并写入单个 detailSnapshotCiphertext | 支持一人多部门和唯一主部门;字段缺失时 INCOMPLETE,禁止查询当前通讯录补写历史 |
| 审批记录查询事实源 | SUP-ADM-012 只读取 supplier_change_log;一条返回记录对应一条 change_log_id,不跨表拼装企微审批详情 | supplier_change_log 为追加式证据表,不得物理删除;审批详情仍由独立审批接口负责 |
| supplierNo 生成规则 | 客户端视为服务端生成的 opaque String;不得请求传入 | 只在 PROFILE_CREATE 最终通过后产生,驳回时为空 |
| 导入一表或两表 | 本期不实现,不影响当前 API | 不得创建占位接口或假成功响应 |
| 物理表名前缀 | 本期物理表统一为详细设计冻结的 supplier_*;Entity、Mapper、DDL、索引和 SQL 必须同名 | 禁止生成 resource_supplier*、resource_* 兼容表/视图或其他额外前缀;若当前源码、migration 或实际 schema 与冻结口径冲突,停止数据库生成并先解决迁移方案 |
10. 验收矩阵
| 接口类型 | 最低场景 | 必须核对 |
|---|---|---|
| 所有管理端接口 | 成功、未认证、角色越权、缺参、边界、目标不存在 | Result code/success、数据库、敏感字段、失败零写入 |
| 查询 | 空结果、分页边界、非法排序、组合筛选 | ID 字符串、默认排序、脱敏、无 N+1 |
| 普通写 | 合法状态、非法状态、expectedUpdateTime 冲突、重复请求 | 锁、事务、字段级日志、零旁路 |
| 资源侧供应商维护 | 资源页面复用供应商列表、查询当前关系、首次设置、显式改绑、解除关系、重复及并发设置、类型或资质不满足、VEHICLE 依赖不可用 | 供应商详情无资源入口;单资源唯一有效关系;失败零写入;解除后可重绑;名称不冗余落表;无跨 schema SQL;Long 使用字符串 |
| 供应商审批记录 | 按 supplierId、approvalLogId、operationType、targetType、fieldName、status、时间范围分页查询 | 仅读取 supplier_change_log;默认 createTime DESC、changeLogId DESC;old/new 只返回脱敏值,不返回审批意见正文 |
| 拉黑/解除 | 合法/非法迁移、LOCAL_AUTO 重复结果、应用失败恢复 | 按 targetStatus 选择 subType;统一结果应用器复核状态机;失败时业务和 DOMAIN_EVENT 零部分写入 |
| 本期 LOCAL_AUTO | 正常通过、Provider 异常、重复结果、APPLY_FAILED 与恢复 | 真实审批行;systemDecision=true;企微字段全空;APPROVED 只经统一结果应用器达到 APPLIED |
| 后续 WECOM | 扫描本期 OpenAPI、源码与配置 | 无 WECOM Feign、回调、模板、对账 Job 或假 spNo;仅保留排除契约 |
| 短窗防重与业务幂等 | 短窗重复请求、事务回滚、服务重启、重复 Provider 结果、APPLY_FAILED 重试 | @Idempotent + 聚合锁 + 状态机 + CAS/唯一约束 + requestNo + 结果应用幂等;不重复业务事实或事件 |
| 账户 | 0/1/N 账户、C-19、默认切换 | 明文不落日志、默认唯一、失败关闭 |
| Internal | 有效/缺失/错误 X-Internal-Token、参数边界、依赖不可用 | 沿用当前内部 Token、失败关闭、无跨 schema 写;本期不新增调用方白名单 |
| 归档 | 清账通过、存在 blocker、Finance 不可用、重复归档 | ARCHIVED 不可逆、历史不断链 |
| 导出 | 扫描本期 OpenAPI、源码与配置 | 无路径、无 DTO/Service/Job、无占位响应 |